﻿using SelElectCalcModernUI.Model;
using SelElectCalcModernUI.Lib;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SelElectCalcModernUI.ViewModel.CalcRes;

namespace SelElectCalcModernUI.Service
{
    public class CalcService
    {
        public static IList<DealRes> Run(double maxPower, double turnoverRatio, IEnumerable<FactoryModel> factories, IEnumerable<SellerModel> sellers)
        {
            return RunNew(maxPower, turnoverRatio, factories, sellers);
        }
        private static IList<DealRes> RunOld(double maxPower, double turnoverRatio, IEnumerable<FactoryModel> factories, IEnumerable<SellerModel> sellers)
        {
            if (factories == null || sellers == null)
                return null;
            var tempfactories = (from c in factories
                                 group c by c.Name into g
                                 select new { Name = g.Key, Power = g.Sum(ss => ss.Power) }).ToDictionary(ss => ss.Name, ss => ss.Power);
            var tempsellers = (from c in sellers
                               group c by c.Name into g
                               select new { Name = g.Key, Power = g.Sum(ss => ss.Power) }).ToDictionary(ss => ss.Name, ss => ss.Power);
            var fatories_inline = factories.OrderByDescending(s => tempfactories[s.Name]).OrderBy(s => s.Price).ToQueue();
            var sellers_inline = sellers.OrderByDescending(s => tempsellers[s.Name]).OrderByDescending(s => s.Price).ToQueue();

            //指定参数
            double deal_powers = 0;      //成交数量累计
            //double deal_powers_max = Math.Min(maxPower, fatories_inline.Sum(s => s.Power) * turnoverRatio);
            double deal_powers_max = Math.Min(maxPower, (fatories_inline.Sum(s => s.Power) + sellers_inline.Sum(s => s.Power)) / 2 * turnoverRatio);   //最大的成交数量
            IList<DealRes> df = new List<DealRes>();// pd.DataFrame(columns = ('发电企业', '申报电量', '申报价差', '售电公司', '申报电量', '申报价差', '价差', '成交电量'))  //数据框初始化
            //int df_i = 0;   //数据框的行指针
            IList<PowerProcess> processes = new List<PowerProcess>();     //累计每个交易流程
            bool flag_stop = false;  //判断是否到达成交比例的flag
            if ((fatories_inline == null) || (sellers_inline == null) ||
                (fatories_inline.Count == 0) || (sellers_inline.Count == 0))
                return df;
            FactoryModel cur_fatory = fatories_inline.Dequeue();
            SellerModel cur_seller = sellers_inline.Dequeue();
            // 交易流程
            while ((fatories_inline.Count > 0 || cur_fatory.Energy_left > 0) &&
                (sellers_inline.Count > 0 || cur_seller.Energy_left > 0))
            {
                var diff_power = cur_fatory.Energy_left - cur_seller.Energy_left;
                var diff_price = cur_fatory.Price - cur_seller.Price;
                // 只有当价差对为负的时候才有可能成交
                if (diff_price > 0)
                    break;
                var deal_power = Math.Min(cur_fatory.Energy_left, cur_seller.Energy_left);
                deal_powers += deal_power;
                // 只有当交易量在规定范围内才有可能成交，否则将flag_stop设置为True
                var curr_del_power = deal_power;
                if (deal_powers > deal_powers_max)
                {
                    curr_del_power += deal_powers_max - deal_powers;
                    flag_stop = true;
                }

                // 针对每次交易过程的数据统计计算
                ////输出信息
                Debug.WriteLine("P:{0} U:{1} P_left_Power:{2} U_left_Power:{3} Deal_Power:{4} Price_Differ:{5}",
                    cur_fatory.Name, cur_seller.Name, cur_fatory.Energy_left,
                    cur_seller.Energy_left, curr_del_power,
                    cur_fatory.Price - cur_seller.Price);
                ////进程信息统计
                processes.Add(new PowerProcess()
                {
                    Power = curr_del_power,
                    Diff_price = 0,
                    P_price = cur_fatory.Price,
                    U_price = cur_seller.Price
                });
                ////累计电费与累计价差电费
                cur_seller.Accu_money += cur_seller.Price * curr_del_power;
                cur_seller.Accu_money_diff += (cur_fatory.Price - cur_seller.Price) * curr_del_power;
                cur_fatory.Accu_money += cur_fatory.Price * curr_del_power;
                cur_fatory.Accu_money_diff += (cur_fatory.Price - cur_seller.Price) * curr_del_power;
                ////数据库信息统计
                df.Add(new DealRes()
                {
                    Factory_name = cur_fatory.Name,
                    Factory_power = cur_fatory.Power,
                    Factory_price = cur_fatory.Price,
                    Seller_name = cur_seller.Name,
                    Seller_power = cur_seller.Power,
                    Seller_price = cur_seller.Price,
                    Del_Power = curr_del_power,
                    Diff_price = cur_fatory.Price - cur_seller.Price,
                    Factory_time = cur_fatory.Times,
                    Seller_time = cur_seller.Times
                }); //[cur_producer.name, cur_producer.amount, cur_producer.price, cur_user.name, cur_user.amount, cur_user.price, cur_producer.price - cur_user.price, curr_del_amount]
                //df_i += 1
                ////判断终止条件
                if (flag_stop)
                {
                    cur_seller.Energy_left -= curr_del_power;
                    cur_fatory.Energy_left -= curr_del_power;
                    break;
                }
                ////判断交易方向，卖者还是买者退出市场
                if (diff_power == 0)
                {
                    cur_seller.Energy_left = 0;
                    cur_fatory.Energy_left = 0;
                    if (fatories_inline.Count == 0 || sellers_inline.Count == 0)
                        break;
                    cur_seller = sellers_inline.Dequeue();
                    cur_fatory = fatories_inline.Dequeue();
                }
                else if (diff_power < 0)  //卖的少，买的多，卖者退出市场
                {
                    cur_seller.Energy_left -= curr_del_power;
                    cur_fatory.Energy_left = 0;
                    if (fatories_inline.Count == 0)
                        break;
                    cur_fatory = fatories_inline.Dequeue();
                }
                else                 //卖的多，买的少，买者退出市场
                {
                    cur_seller.Energy_left = 0;
                    cur_fatory.Energy_left -= curr_del_power;
                    if (sellers_inline.Count == 0)
                        break;
                    cur_seller = sellers_inline.Dequeue();
                }
            }
            return df;
        }
        private static IList<DealRes> RunNew(double maxPower, double turnoverRatio, IEnumerable<FactoryModel> factories, IEnumerable<SellerModel> sellers)
        {
            if (factories == null || sellers == null)
                return null;
            var factoryPriceGroup = (from c in factories
                                     group c by c.Price into g
                                     select new PriceGroup<FactoryModel>()
                                     {
                                         Price = g.Key,
                                         Power = g.Sum(s => s.Power),
                                         Energy_left = g.Sum(s => s.Power),
                                         Accu_money = 0,
                                         Accu_money_diff = 0,
                                         Childs = g.ToList()
                                     }).OrderBy(s => s.Price).ToQueue();
            var sellerPriceGroup = (from c in sellers
                                    group c by c.Price into g
                                    select new PriceGroup<SellerModel>()
                                    {
                                        Price = g.Key,
                                        Power = g.Sum(s => s.Power),
                                        Energy_left = g.Sum(s => s.Power),
                                        Accu_money = 0,
                                        Accu_money_diff = 0,
                                        Childs = g.ToList(),
                                    }).OrderByDescending(s => s.Price).ToQueue();
            //指定参数
            double deal_powers = 0;      //成交数量累计
            //double deal_powers_max = Math.Min(maxPower, factoryPriceGroup.Sum(s => s.Power) * turnoverRatio);
            double deal_powers_max = Math.Min(maxPower, (factoryPriceGroup.Sum(s => s.Power) + sellerPriceGroup.Sum(s => s.Power)) / 2 * turnoverRatio);   //最大的成交数量
            IList<DealRes> df = new List<DealRes>();  //数据框初始化

            bool flag_stop = false;  //判断是否到达成交比例的flag
            if ((factoryPriceGroup == null) || (sellerPriceGroup == null) ||
                (factoryPriceGroup.Count == 0) || (sellerPriceGroup.Count == 0))
                return df;
            PriceGroup<FactoryModel> cur_fatory = factoryPriceGroup.Dequeue();
            PriceGroup<SellerModel> cur_seller = sellerPriceGroup.Dequeue();
            int count = 0;
            // 交易流程
            while ((factoryPriceGroup.Count > 0 || cur_fatory.Energy_left > 0) &&
                (sellerPriceGroup.Count > 0 || cur_seller.Energy_left > 0))
            {
                var diff_power = cur_fatory.Energy_left - cur_seller.Energy_left;
                var diff_price = cur_fatory.Price - cur_seller.Price;
                count++;
                // 只有当价差对为负的时候才有可能成交
                if (diff_price > 0)
                    break;
                var deal_power = Math.Min(cur_fatory.Energy_left, cur_seller.Energy_left);
                deal_powers += deal_power;
                // 只有当交易量在规定范围内才有可能成交，否则将flag_stop设置为True
                var curr_del_power = deal_power;
                if (deal_powers >= deal_powers_max)
                {
                    curr_del_power += deal_powers_max - deal_powers;
                    flag_stop = true;
                }

                ////累计电费与累计价差电费
                cur_seller.Accu_money += cur_seller.Price * curr_del_power;
                cur_seller.Accu_money_diff += (cur_fatory.Price - cur_seller.Price) * curr_del_power;
                cur_fatory.Accu_money += cur_fatory.Price * curr_del_power;
                cur_fatory.Accu_money_diff += (cur_fatory.Price - cur_seller.Price) * curr_del_power;

                //按比例平均分配交易
                ScaleEach(curr_del_power, cur_seller.Childs, cur_fatory.Childs, df, count);

                ////判断终止条件
                if (flag_stop)
                {
                    cur_seller.Energy_left -= curr_del_power;
                    cur_fatory.Energy_left -= curr_del_power;
                    break;
                }
                ////判断交易方向，卖者还是买者退出市场
                if (diff_power == 0)
                {
                    cur_seller.Energy_left = 0;
                    cur_fatory.Energy_left = 0;
                    if (factoryPriceGroup.Count == 0 || sellerPriceGroup.Count == 0)
                        break;
                    cur_seller = sellerPriceGroup.Dequeue();
                    cur_fatory = factoryPriceGroup.Dequeue();
                }
                else if (diff_power < 0)  //卖的少，买的多，卖者退出市场
                {
                    cur_seller.Energy_left -= curr_del_power;
                    cur_fatory.Energy_left = 0;
                    if (factoryPriceGroup.Count == 0)
                        break;
                    cur_fatory = factoryPriceGroup.Dequeue();
                }
                else                 //卖的多，买的少，买者退出市场
                {
                    cur_seller.Energy_left = 0;
                    cur_fatory.Energy_left -= curr_del_power;
                    if (sellerPriceGroup.Count == 0)
                        break;
                    cur_seller = sellerPriceGroup.Dequeue();
                }
            }
            double totDeal = 0;
            foreach (var item in df)
            {
                totDeal += item.Del_Power;
                item.Tot_power = totDeal;
            }
            return df;
        }
        private static void ScaleEach(double totalPower, IList<SellerModel> sellers, IList<FactoryModel> factories, IList<DealRes> df, int count)
        {
            var filter0Seller = from c in sellers where c.Power > 0 select c;
            var filter0Factory = from c in factories where c.Power > 0 select c;
            double factorySum = filter0Factory.Sum(s => s.Power);
            double sellerSum = filter0Seller.Sum(s => s.Power);
            foreach (var cur_fatory in filter0Factory)
            {
                foreach (var cur_seller in filter0Seller)
                {
                    double rate = cur_seller.Power / sellerSum * cur_fatory.Power / factorySum;
                    Debug.WriteLine("P:{0} U:{1} P_left_Power:{2} U_left_Power:{3} Deal_Power:{4} Price_Differ:{5}",
                        cur_fatory.Name, cur_seller.Name, cur_fatory.Energy_left,
                        cur_seller.Energy_left, totalPower * rate,
                        cur_fatory.Price - cur_seller.Price);
                    df.Add(new DealRes()
                    {
                        Factory_name = cur_fatory.Name,
                        Factory_power = cur_fatory.Power,
                        Factory_price = cur_fatory.Price,
                        Seller_name = cur_seller.Name,
                        Seller_power = cur_seller.Power,
                        Seller_price = cur_seller.Price,
                        Del_Power = totalPower * rate,
                        Diff_price = cur_fatory.Price - cur_seller.Price,
                        Factory_time = cur_fatory.Times,
                        Seller_time = cur_seller.Times,
                        Factory_left_power = cur_fatory.Energy_left - totalPower * rate,
                        Factory_this_power = cur_fatory.Energy_left,
                        Enable_factory = true,
                        Seller_left_power = cur_seller.Energy_left - totalPower * rate,
                        Seller_this_power = cur_seller.Energy_left,
                        Enable_seller = true,
                        Index = count,
                        Tot_power = 0,
                    });
                    cur_fatory.Energy_left -= totalPower * rate;
                    cur_seller.Energy_left -= totalPower * rate;
                }
            }
        }
        private static void ScaleEach(double totalPower, IList<SellerModel> sellers, IList<FactoryModel> factories, IList<DealRes> df)
        {
            ScaleEach(totalPower, sellers, factories, df, -1);
        }
        public static IEnumerable<FactoryResSignle> GetFactories(IList<DealRes> list, double factoryRange)
        {
            var tempListGroup = from c in list group c by c.Factory_name into g select g;
            return FillFactoryList(list, factoryRange, tempListGroup);
        }
        public static IEnumerable<FactoryResSignle> GetFactoriesDetail(IList<DealRes> list, double factoryRange)
        {
            var tempListGroup = from c in list group c by new { factory_name = c.Factory_name, time = c.Factory_time } into g select g;
            return FillFactoryList(list, factoryRange, tempListGroup);
        }

        private static IEnumerable<FactoryResSignle> FillFactoryList<T>(IList<DealRes> list, double factoryRange, IEnumerable<IGrouping<T, DealRes>> tempListGroup)
        {
            double factoryPrice = list.Sum(s => s.Factory_price * s.Del_Power);
            double fback = list.Sum(s => s.Diff_price * factoryRange * s.Del_Power);
            double totPower = list.Sum(s => s.Del_Power);
            var res = new List<FactoryResSignle>();
            foreach (var item in tempListGroup)
            {
                FactoryResSignle resItem = new FactoryResSignle();
                resItem.Factory = item.First().Factory_name;
                resItem.OrgPower = item.GroupBy(s => s.Factory_time).Sum(s => s.First().Factory_power);//item.Sum(s=>s.Factory_power);
                resItem.RealPower = item.Sum(s => s.Del_Power);
                resItem.OrgPrice = item.Sum(s => s.Factory_power * s.Factory_price) / item.Sum(s => s.Factory_power);
                resItem.Seller = item.FirstOrDefault().Seller_name;
                resItem.SurplusPower = resItem.OrgPower - resItem.RealPower;
                resItem.Times = item.First().Factory_time;
                resItem.TotDifPrice = -item.Sum(s => s.Del_Power * (s.Diff_price));
                resItem.TotBackPrice = -item.Sum(s => s.Del_Power * s.Factory_price) / factoryPrice * fback;
                resItem.TotNoBackPrice = -resItem.RealPower * resItem.OrgPrice;
                resItem.TotPriceRange = -resItem.TotNoBackPrice / list.Sum(s => s.Factory_price * s.Del_Power);
                resItem.TotPrice = resItem.TotNoBackPrice - resItem.TotBackPrice;
                resItem.RealPrice = -resItem.TotPrice / item.Sum(s => s.Del_Power);
                resItem.TotIn = resItem.RealPower * 0.5 - resItem.TotPrice;
                resItem.PerIn = resItem.TotIn / resItem.RealPower;

                resItem.AvgOrgPrice = resItem.OrgPrice;
                resItem.AvgBackPrice = resItem.TotBackPrice / resItem.RealPower;
                resItem.InSeat = 0;
                resItem.DealPer = resItem.RealPower / resItem.OrgPower;
                resItem.DealTotPer = resItem.RealPower / totPower;
                resItem.DealTotPerSeat = 0;
                res.Add(resItem);
            }
            var tempList = res.OrderByDescending(s => s.PerIn);
            int seat = 0;
            double last = double.NaN;
            foreach (var temp in tempList)
            {
                double value = Math.Round(temp.PerIn, 4);
                if (value != last)
                {
                    temp.InSeat = ++seat;
                    last = value;
                }
                else
                    temp.InSeat = seat;
            }
            tempList = res.OrderByDescending(s => s.DealTotPer);
            seat = 0;
            last = double.NaN;
            foreach (var temp in tempList)
            {
                double value = Math.Round(temp.DealTotPer, 4);
                if (value != last)
                {
                    temp.DealTotPerSeat = ++seat;
                    last = value;
                }
                else
                    temp.DealTotPerSeat = seat;
            }
            return res;
        }

        public static IEnumerable<SellerResSignle> GetSellers(IList<DealRes> list, double sellerRange)
        {
            var tempListGroup = from c in list group c by c.Seller_name into g select g;
            return FillSellerList(list, sellerRange, tempListGroup);
        }

        public static IEnumerable<SellerResSignle> GetSellersDetail(IList<DealRes> list, double sellerRange)
        {
            var tempListGroup = from c in list group c by new { seller_name = c.Seller_name, time = c.Seller_time } into g select g;
            return FillSellerList(list, sellerRange, tempListGroup);
        }
        private static IEnumerable<SellerResSignle> FillSellerList<T>(IList<DealRes> list, double sellerRange, IEnumerable<IGrouping<T, DealRes>> tempListGroup)
        {
            double sellerPrice = list.Sum(s => s.Seller_price * s.Del_Power);
            double fback = list.Sum(s => s.Diff_price * sellerRange * s.Del_Power);
            double totPower = list.Sum(s => s.Del_Power);
            var res = new List<SellerResSignle>();
            foreach (var item in tempListGroup)
            {
                SellerResSignle resItem = new SellerResSignle();
                resItem.Seller = item.First().Seller_name;
                resItem.OrgPower = item.GroupBy(s => s.Seller_time).Sum(s => s.First().Seller_power);
                resItem.RealPower = item.Sum(s => s.Del_Power);
                resItem.OrgPrice = item.Sum(s => s.Seller_power * s.Seller_price) / item.Sum(s => s.Seller_power);
                resItem.Factory = item.FirstOrDefault().Factory_name;
                resItem.SurplusPower = resItem.OrgPower - resItem.RealPower;
                resItem.Times = item.First().Seller_time;
                resItem.TotDifPrice = -item.Sum(s => s.Del_Power * (s.Diff_price));
                resItem.TotBackPrice = -item.Sum(s => s.Del_Power * s.Seller_price) / sellerPrice * fback;
                resItem.TotNoBackPrice = -resItem.RealPower * resItem.OrgPrice;
                resItem.TotPriceRange = -resItem.TotNoBackPrice / list.Sum(s => s.Seller_price * s.Del_Power);
                resItem.TotPrice = resItem.TotBackPrice + resItem.TotNoBackPrice;
                resItem.RealPrice = -resItem.TotPrice / item.Sum(s => s.Del_Power);
                resItem.TotIn = resItem.TotPrice;
                resItem.PerIn = resItem.TotIn / resItem.RealPower;
                resItem.AvgOrgPrice = resItem.OrgPrice;
                resItem.AvgBackPrice = resItem.TotBackPrice / resItem.RealPower;
                resItem.InSeat = 0;
                resItem.DealPer = resItem.RealPower / resItem.OrgPower;
                resItem.DealTotPer = resItem.RealPower / totPower;
                resItem.DealTotPerSeat = 0;
                res.Add(resItem);
            }
            var tempList = res.OrderByDescending(s => s.PerIn);
            int seat = 0;
            double last = double.NaN;
            foreach (var temp in tempList)
            {
                double value = Math.Round(temp.PerIn, 4);
                if (value != last)
                {
                    temp.InSeat = ++seat;
                    last = value;
                }
                else
                    temp.InSeat = seat;
            }
            tempList = res.OrderByDescending(s => s.DealTotPer);
            seat = 0;
            last = double.NaN;
            foreach (var temp in tempList)
            {
                double value = Math.Round(temp.DealTotPer, 4);
                if (value != last)
                {
                    temp.DealTotPerSeat = ++seat;
                    last = value;
                }
                else
                    temp.DealTotPerSeat = seat;
            }
            return res;
        }
    }
}
